<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>DocumentFragment</title>
</head>
<body>
<div id="out"></div>

<h2>Summary</h2>
<pre>

1. Chrome、Safari、Firefox、Opera 下，script 添加到 fragment 中时不会下载，添加
   到 DOM 树中时才会下载并执行，并且执行上下文是全局环境，一切符合预期。

2. IE6-9 下，在 script 设置 src 属性时就会触发下载，添加到 fragment 或其他 DOM 节点
   时会触发执行。执行时上下文是 fragment，相当于

       with(fragment) { (function() { script code }).call(fragment) }

   当有缓存时，情况更复杂，详见参考文档 [3]

3. 标准浏览器包括 IE9+ 下，fragment 没有 createElement 等方法，但在 IE6-8 下有。

4. IE10 与 Chrome 等标准浏览器表现一致，赞。


本来想利用 DocumentFragment 来解决 IE6-9 下获取 script 的 src 的问题，比如：

    fragment['define'] = function(require, exports, module) {
      // 很自然地得到了模块 id
      module.id = script.src

      // 调用 seajs 的 define
      window.define(require, exports, module)
    }

现在看起来带来的隐患很多。如果用 DocumentFragment 来动态插入 script，则 script code
在执行时，相当于

       with(fragment) { (function() { script code }).call(fragment) }

   a) script code 执行时多了一层闭包，这导致 var xx = ... 不再是全局变量。
   b) script code 执行时的 this 是 fragment，不是 window，这会带来很多问题。
   c) 多了一层闭包，如果 script code 代码不当，会导致内存泄露。

这样，利用 DocumentFragment 来动态插入 script，只适合纯数据的 JSONP，比如

   define({
     "a": "a",
     ...
   })

上面这种情况下，利用 DocumentFragment 来实现 JSONP，简单方便，并能提升性能。


补充：不用 DocumentFragment 做容器，用普通 div 元素做容器，IE6-9 下会自动创建一个匿名
     document，执行时变成

       with(document) { (function() { script code }).call(document) }

     问题依旧。


最后更新时间：2012-12-17


参考文档：

  - https://developer.mozilla.org/en-US/docs/DOM/document.createDocumentFragment
  - http://www.nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/
  - http://www.guypo.com/technical/ies-premature-execution-problem/
  - http://velocity.oreilly.com.cn/2012/ppts/xuxiao.pdf
  - http://varnow.org/?p=152
  - http://www.cnblogs.com/objectorl/archive/2009/08/05/1632718.html
  - http://www.cnblogs.com/rubylouvre/archive/2012/12/16/2820372.html
  - http://www.webshowme.com/04js/content.asp?id=1613

</pre>

<div id="t"></div>
<script src="../../test.js"></script>
<script>

  function printResults(msg) {
    var p = document.createElement('P')
    p.appendChild(document.createTextNode(msg))
    document.getElementById('out').appendChild(p)
  }

  function assertV(name, expected) {
    var val = this[name]
    test.assert(val === expected, name + ' = ' + val)
  }


  var X = 'x'
  var head = document.getElementsByTagName('head')[0]

  var container = document.createDocumentFragment()
  //var container = document.createElement('div')
  //var container = document.getElementById('t')

  container.X = 'container-x'
  container.Y = 'container-y'
  test.print('container.createElement = ' + (typeof container.createElement))

  //var script = frag.createElement('script')
  var script = document.createElement('script')
  script.src = 'test.js'

  container.appendChild(script)
  test.print('----- After appendTo Container -----')

  setTimeout(function() {
    assertV('X', 'x')
    assertV('Y', undefined)
    assertV('Z', undefined)

    head.appendChild(container)
    test.print('----- After appendTo Document -----')

    setTimeout(function() {
      assertV('X', 'x')
      assertV('Y', undefined)
      assertV('Z', 'script-z')

      test.assert(window.Z === 'script-z', 'window.Z = ' + window.Z)
      test.assert(container.X === 'container-x', 'container.X = ' + container.X)
      test.assert(container.Y === 'container-y', 'container.Y = ' + container.Y)
      test.assert(container.Z === undefined, 'container.Z = ' + container.Z)
    }, 1000)

  }, 1000)

</script>
</body>
</html>
